package com.yerbamateprimer.action.user.impl;

import java.io.Serializable;

import org.apache.commons.lang.builder.ReflectionToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;

import com.google.common.base.Preconditions;
import com.yerbamateprimer.action.user.SynchronousUserAction;
import com.yerbamateprimer.advice.impl.DeliverTheMateAdvice;
import com.yerbamateprimer.advice.impl.DrinkTheMateAdvice;
import com.yerbamateprimer.model.User;
import com.yerbamateprimer.round.Round;
import com.yerbamateprimer.round.state.StateName;
import com.yerbamateprimer.service.ClientService;
import com.yerbamateprimer.service.RoundService;

/**
 * This action is useful to skip
 * the current drinker turn to
 * drink.
 * 
 * @author Matias Liwski
 * @version 1.0
 */
public class SkipDrinkerUserAction implements SynchronousUserAction, Serializable {
	private static final long serialVersionUID = -2864856412698752630L;
	private User user;
	private transient RoundService roundService;
	private transient ClientService clientService;

	/**
	 * Default constructor, used by the serializer.
	 */
	public SkipDrinkerUserAction() {
	}

	/**
	 * @param user The user that want to execute this action.
	 */
	public SkipDrinkerUserAction(User user) {
		setUser(user);
	}

	/* (non-Javadoc)
	 * @see com.yerbamateprimer.action.user.UserAction#execute()
	 */
	@Override
	public void execute() {
		checkArgumentPreconditions();
		//Check action needed preconditions
		Round round = this.roundService.getRoundForUser(this.user);
		Preconditions.checkState(round != null, "The user: " + this.user + " isn't participating at any round.");
		User primer = round.getPrimer();
		User currentDrinker = round.getCurrentDrinker();
		StateName stateName = round.getCurrentState().getName();
		Preconditions.checkArgument(this.user.equals(primer) || this.user.equals(currentDrinker), "Only the primer or the current drinker could skip a user turn ( user: " + this.user + ", primer: " + primer + ", drinker: " + currentDrinker + ").");
		Preconditions.checkState(StateName.DRINK.equals(stateName), "Only could skip the drinker at DRINKING state ( stateName: " + stateName + " ).");
		
		User newDrinker = round.rotateCurrentDrinker();
		this.clientService.send(new DeliverTheMateAdvice(this.user, newDrinker));
		this.clientService.send(new DrinkTheMateAdvice(newDrinker, this.user));
	}

	/**
	 * Check the preconditions of the arguments needed to execute this action.
	 */
	private void checkArgumentPreconditions() {
		Preconditions.checkArgument(this.user != null, "This action need a user to execute it.");
		Preconditions.checkArgument(this.roundService != null, "This action need a round service to execute it.");
		Preconditions.checkArgument(this.clientService != null, "This action needs a client service to execute it.");
	}

	/* (non-Javadoc)
	 * @see com.yerbamateprimer.action.user.UserAction#setUser(com.yerbamateprimer.model.User)
	 */
	@Override
	public void setUser(User user) {
		Preconditions.checkArgument(user != null, "This action needs a user to execute it.");
		this.user = user;
	}

	/* (non-Javadoc)
	 * @see com.yerbamateprimer.action.user.UserAction#getUser()
	 */
	@Override
	public User getUser() {
		return this.user;
	}

	/**
	 * @param roundService The round service of the Action, to set.
	 */
	public void setRoundService(RoundService roundService) {
		Preconditions.checkArgument(roundService != null, "This action needs a round service to execute it.");
		this.roundService = roundService;
	}

	/**
	 * @return The roundService of the Action.
	 */
	public RoundService getRoundService() {
		return this.roundService;
	}

	/**
	 * @param clientService The client service of the Action, to set.
	 */
	public void setClientService(ClientService clientService) {
		Preconditions.checkArgument(clientService != null, "This action needs a client service to execute it.");
		this.clientService = clientService;
	}

	/**
	 * @return The client service of the Action.
	 */
	public ClientService getClientService() {
		return this.clientService;
	}

	/* (non-Javadoc)
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString() {
		return ReflectionToStringBuilder.toString(this, ToStringStyle.SHORT_PREFIX_STYLE, false, true);
	}
}